package com.zc.auth.security.core.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.zc.auth.security.core.authentication.AuthenticationTokenService;
import com.zc.commons.http.entity.Response;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * Webflux权限校验过滤器
 *
 * @author zcj
 * @version 1.0.0
 * @date 2022/1/11 16:31
 */
public class WebFluxAuthenticationFilter implements WebFilter {

    private AuthenticationTokenService authenticationTokenService;

    public WebFluxAuthenticationFilter() {
    }

    public WebFluxAuthenticationFilter(AuthenticationTokenService authenticationTokenService) {
        this.authenticationTokenService = authenticationTokenService;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
        ServerHttpRequest request = serverWebExchange.getRequest();
        HttpHeaders headers = request.getHeaders();
        List<String> list = headers.get(HttpHeaders.AUTHORIZATION);
        // token解析判断凭证是否可用
        Authentication authenticationToken;
        try {
            authenticationToken = authenticationTokenService.getAuthentication(CollectionUtils.isEmpty(list) ? null : list.get(0));
        } catch (AuthenticationException e) {
            return tokenExceptionHandle(serverWebExchange, e);
        }
        // 重新构建在链中传递
        ServerWebExchange exchange = serverWebExchange.mutate().principal(Mono.just(authenticationToken)).build();
        return webFilterChain.filter(exchange)
                .subscriberContext(ReactiveSecurityContextHolder.withAuthentication(authenticationToken));
    }

    /**
     * token解析或者无效处理
     *
     * @param serverWebExchange
     * @param ex
     * @return
     */
    public Mono<Void> tokenExceptionHandle(ServerWebExchange serverWebExchange, AuthenticationException ex) {
        ServerHttpResponse response = serverWebExchange.getResponse();
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        HttpStatus httpStatus = HttpStatus.UNAUTHORIZED;
        response.setStatusCode(httpStatus);
        Response<String> respData = Response.error(ex.getMessage());
        DataBuffer buffer = response.bufferFactory().wrap(JSON.toJSONString(respData, SerializerFeature.WriteMapNullValue).getBytes());
        return response.writeWith(Mono.just(buffer));
    }


}
